#include "Rp2Spatial.h"
#include "Rp2Camera.h"
#include "Rp2Culler.h"
#include "Rp2Light.h"

using namespace Rp2;

RP2_IMPLEMENT_RTTI(Rp2, Spatial, Object);
RP2_IMPLEMENT_DEFAULT_NAME_ID(Spatial, Object);

//---------------------------------------------------------------------------------------------------
Spatial::Spatial()
	:
	WorldBound(BoundingVolume::Create())
{
	Culling = CULL_DYNAMIC;
	WorldIsCurrent = false;
	WorldBoundIsCurrent = false;
	m_pkParent = 0;	
	m_iStartEffect = 0;
}
//---------------------------------------------------------------------------------------------------
Spatial::~Spatial()
{
	DetachAllGlobalStates();
	DetachAllEffects();
	DetachAllLights();
}
//---------------------------------------------------------------------------------------------------
void Spatial::UpdateGS(double dAppTime, bool bInitiator)
{
	UpdateWorldData(dAppTime);
	UpdateWorldBound();
	if (bInitiator)
	{
		PropagateBoundToRoot();
	}
}
//---------------------------------------------------------------------------------------------------
void Spatial::UpdateBS()
{
	UpdateWorldBound();
	PropagateBoundToRoot();
}
//---------------------------------------------------------------------------------------------------
void Spatial::PropagateBoundToRoot()
{
	if (m_pkParent)
	{
		m_pkParent->UpdateWorldBound();
		m_pkParent->PropagateBoundToRoot();
	}
}
//---------------------------------------------------------------------------------------------------
void Spatial::UpdateWorldData(double dAppTime)
{
	// update any controllers associated with this object
	UpdateControllers(dAppTime);

	int i;
	for (i = 0; i < (int)m_kGlobalStates.size(); i++)
	{
		m_kGlobalStates[i]->UpdateControllers(dAppTime);
	}

	for (i = 0; i < (int)m_kLights.size(); i++)
	{
		m_kLights[i]->UpdateControllers(dAppTime);
	}

	if (!WorldIsCurrent)
	{
		if (m_pkParent)
		{
			World.Product(m_pkParent->World, Local);
		}
		else
		{
			World = Local;
		}
	}
}
//---------------------------------------------------------------------------------------------------
void Spatial::UpdateRS(std::vector<GlobalState*>* akGStack, std::vector<Light*>* pkLStack)
{
	bool bInitiator = (akGStack == 0);

	if (bInitiator)
	{
        // The order of preference is
        //   (1) Default global states are used.
        //   (2) Geometry can override them, but if global state FOOBAR
        //       has not been pushed to the Geometry leaf node, then
        //       the current FOOBAR remains in effect (rather than the
        //       default FOOBAR being used).
        //   (3) Effect can override default or Geometry render states.
		akGStack = new std::vector<GlobalState*>[GlobalState::MAX_STATE_TYPE];
		for (int i = 0; i < GlobalState::MAX_STATE_TYPE; i++)
		{
			akGStack[i].push_back(0);
		}

        // stack has no lights initially
        pkLStack = new std::vector<Light*>;

        // traverse to root and push states from root to this node
		PropagateStateFromRoot(akGStack, pkLStack);
	}
	else
	{
		// push state at this node
		PushState(akGStack, pkLStack);
	}

	// propagate the new state to the subtree rooted here
	UpdateState(akGStack, pkLStack);

	if (bInitiator)
	{
		delete[] akGStack;
		delete pkLStack;
	}
	else
	{
		PopState(akGStack, pkLStack);
	}
}
//---------------------------------------------------------------------------------------------------
void Spatial::PropagateStateFromRoot(std::vector<GlobalState*>* akGStack,
									 std::vector<Light*>* pkLStack)
{
	if (m_pkParent)
	{
		m_pkParent->PropagateStateFromRoot(akGStack, pkLStack);
	}

	// push states onto current render state stack
	PushState(akGStack, pkLStack);
}
//---------------------------------------------------------------------------------------------------
void Spatial::PushState(std::vector<GlobalState*>* akGStack, std::vector<Light*>* pkLStack)
{
	int i;
	for (i = 0; i < (int)m_kGlobalStates.size(); i++)
	{
		int eType = m_kGlobalStates[i]->GetStateType();
		akGStack[eType].push_back(m_kGlobalStates[i]);
	}

	for (i = 0; i < (int)m_kLights.size(); i++)
	{
		Light* pkLight = StaticCast<Light>(m_kLights[i]);
		pkLStack->push_back(pkLight);
	}
}
//---------------------------------------------------------------------------------------------------
void Spatial::PopState(std::vector<GlobalState*>* akGStack, std::vector<Light*>* pkLStack)
{
	int i;
	for (i = 0; i < (int)m_kGlobalStates.size(); i++)
	{
		int eType = m_kGlobalStates[i]->GetStateType();
		akGStack[eType].pop_back();
	}

    for (i = 0; i < (int)m_kLights.size(); i++)
    {
        pkLStack->pop_back();
    }
}
//---------------------------------------------------------------------------------------------------
GlobalState* Spatial::GetGlobalState(GlobalState::StateType eType) const
{
	for (int i = 0; i < (int)m_kGlobalStates.size(); i++)
	{
		if (m_kGlobalStates[i]->GetStateType() == eType)
		{
			return m_kGlobalStates[i];
		}
	}
	return 0;
}
//---------------------------------------------------------------------------------------------------
void Spatial::AttachGlobalState(GlobalState* pkState)
{
	assert(pkState);

	// check if this type of state already in the list
	for (int i = 0; i < (int)m_kGlobalStates.size(); i++)
	{
		if (m_kGlobalStates[i]->GetStateType() == pkState->GetStateType())
		{
			// this type of state already exists. so replace it
			m_kGlobalStates[i] = pkState;
			return;
		}
	}

	// This type of state is not in the current list. so add it 
	m_kGlobalStates.push_back(pkState);
}
//---------------------------------------------------------------------------------------------------
void Spatial::DetachGlobalState(GlobalState::StateType eType)
{
	std::vector<GlobalStatePtr>::iterator pkIter = m_kGlobalStates.begin();
	for ( ; pkIter != m_kGlobalStates.end(); pkIter++)
	{
		GlobalState* pkState = *pkIter;
		if (pkState->GetStateType() == eType)
		{
			m_kGlobalStates.erase(pkIter);
			return;
		}
	}
}
//---------------------------------------------------------------------------------------------------
void Spatial::AttachLight (Light* pkLight)
{
    assert(pkLight);

    // Check if the light is already in the list.
    for (int i = 0; i < (int)m_kLights.size(); i++)
    {
        if (m_kLights[i] == pkLight)
        {
            // The light already exists, so do nothing.
            return;
        }
    }

    // The light is not in the current list, so add it.
    m_kLights.push_back(pkLight);
}
//---------------------------------------------------------------------------------------------------
void Spatial::DetachLight (Light* pkLight)
{
    std::vector<ObjectPtr>::iterator pkIter = m_kLights.begin();
    for (/**/; pkIter != m_kLights.end(); pkIter++)
    {
        if (pkLight == *pkIter)
        {
            m_kLights.erase(pkIter);
            return;
        }
    }
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
void Spatial::AttachEffect(Effect* pkEffect)
{
	assert(pkEffect);

	// if the effect already in the list
	for (int i = 0; i < (int)m_kEffects.size(); i++)
	{
		if (m_kEffects[i] == pkEffect)
		{
			// the effects already exist
			return;
		}
	}

	// the effect is not exist, add it
	m_kEffects.push_back(pkEffect);
}
//---------------------------------------------------------------------------------------------------
void Spatial::DetachEffect (Effect* pkEffect)
{
    std::vector<EffectPtr>::iterator pkIter = m_kEffects.begin();
    for (/**/; pkIter != m_kEffects.end(); pkIter++)
    {
        if (pkEffect == *pkIter)
        {
            m_kEffects.erase(pkIter);
            return;
        }
    }
}
//---------------------------------------------------------------------------------------------------
void Spatial::OnGetVisibleSet (Culler& rkCuller, bool bNoCull)
{
    if (Culling == CULL_ALWAYS)
    {
        return;
    }

    if (Culling == CULL_NEVER)
    {
        bNoCull = true;
    }

    unsigned int uiSavePlaneState = rkCuller.GetPlaneState();
    if (bNoCull || rkCuller.IsVisible(WorldBound))
    {
        GetVisibleSet(rkCuller,bNoCull);
    }
    rkCuller.SetPlaneState(uiSavePlaneState);
}
//---------------------------------------------------------------------------------------------------